package jp.co.jbuy.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 * コネクションプール対応のコネクション生成クラス
 *
 * 基本的にstaticにオブジェクトをセットして使いまわししてください。
 * 使用後はreleaseを呼ぶ必要があります。(finally句で呼び出すことを推奨)
 * また、プログラム終了時は必ずreleaseAllを呼ぶ事。
 */
public class ConnectionManager {
	private static final int DONT_SET_TRANSACTION_ISOLATION = -10000;
	private ArrayList<Connection> connPool_;
	private String driver_;
	private String url_;
	private String username_;
	private String password_;
	private boolean isAutoCommit_;
	private int transactionIsolationLevel_;
	private int maxPoolSize_;

	/**
	 * コンストラクタ
	 * @param maxPoolSize
	 */
	public ConnectionManager(String driver, String url, String username,
							String password, int maxPoolSize, boolean isAutoCommit,
							String transactionIsolationLevel) {
		this.connPool_ = new ArrayList<Connection>();
		this.driver_ = driver;
		this.url_ = url;
		this.username_ = username;
		this.password_ = password;
		this.isAutoCommit_=isAutoCommit;
		if(transactionIsolationLevel==null) {
			transactionIsolationLevel_ = DONT_SET_TRANSACTION_ISOLATION;
		} else if(transactionIsolationLevel.equals("NONE")) {
			this.transactionIsolationLevel_ = Connection.TRANSACTION_NONE;
		} else if(transactionIsolationLevel.equals("READ_COMMITTED")) {
			this.transactionIsolationLevel_ = Connection.TRANSACTION_READ_COMMITTED;
		} else if(transactionIsolationLevel.equals("READ_UNCOMMITTED")) {
			this.transactionIsolationLevel_ = Connection.TRANSACTION_READ_UNCOMMITTED;
		} else if(transactionIsolationLevel.equals("REPEATABLE_READ")) {
			this.transactionIsolationLevel_ = Connection.TRANSACTION_REPEATABLE_READ;
		} else if(transactionIsolationLevel.equals("SERIALIZABLE")) {
			this.transactionIsolationLevel_ = Connection.TRANSACTION_SERIALIZABLE;
		} else {
			transactionIsolationLevel_ = DONT_SET_TRANSACTION_ISOLATION;
		}
		if(maxPoolSize > -1) {
			this.maxPoolSize_ = maxPoolSize;
		} else {
			this.maxPoolSize_ = 0;
		}
	}

	/**
	 * コネクションを取得するメソッド
	 * @return Connection
	 */
	public synchronized Connection getConnection() {
		Connection conn = null;
		try {
			if(connPool_.size() > 0) {
				conn = (Connection) connPool_.get(0);
				connPool_.remove(0);
				if(conn.isClosed()) {
					this.getConnection();
				}
			} else {
				try {
					Class.forName(driver_);
					conn = DriverManager.getConnection(url_, username_, password_);
					conn.setAutoCommit(isAutoCommit_);
					if(transactionIsolationLevel_ != DONT_SET_TRANSACTION_ISOLATION) {
						conn.setTransactionIsolation(transactionIsolationLevel_);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
			this.releaseAll();
		}
		return conn;
	}
	
	/**
	 * 使用後のコネクションをPoolに戻す。
	 * ただし、Pool内のコネクション数が最大値以上の場合はそのままcloseする
	 * @param conn
	 */
	public synchronized void release(Connection conn) {
		try {
			if(conn!=null && !conn.isClosed() && conn.getAutoCommit()) {
				conn.commit();
			}
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		if(conn!=null && connPool_.size()<maxPoolSize_) {
			connPool_.add(conn);
		} else {
			try {
				if(conn!=null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * プログラムが停止する時に呼ぶ
	 * Pool上のコネクションをすべて開放する。
	 */
	public synchronized void releaseAll() {
		for(Connection conn : connPool_) {
			try {
				if(conn!=null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}
